package org.lc.stk.security.exception;

import org.lc.stk.web.response.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;

/**
 * 全局异常处理器
 *
 * <p>统一处理系统中的各类异常,将其转换为标准的错误响应格式。支持以下异常类型:</p>
 * <ul>
 *   <li>认证相关异常:
 *     <ul>
 *       <li>{@link JwtAuthenticationException}: JWT令牌相关错误</li>
 *       <li>{@link BadCredentialsException}: 用户名密码错误</li>
 *       <li>{@link UsernameNotFoundException}: 用户不存在</li>
 *     </ul>
 *   </li>
 *   <li>请求参数验证异常:
 *     <ul>
 *       <li>{@link MethodArgumentNotValidException}: 请求参数校验失败</li>
 *     </ul>
 *   </li>
 *   <li>其他未处理异常:
 *     <ul>
 *       <li>{@link Exception}: 捕获所有未明确处理的异常</li>
 *     </ul>
 *   </li>
 * </ul>
 *
 * <p>标准错误响应格式({@link ErrorResponse}):</p>
 * <pre>
 * {
 *   "status": 401,        // HTTP状态码
 *   "message": "认证失败",  // 错误概要
 *   "detail": "令牌已过期"  // 详细错误信息
 * }
 * </pre>
 *
 * <p>错误码规范:</p>
 * <ul>
 *   <li>401 Unauthorized: 认证相关错误</li>
 *   <li>404 Not Found: 资源未找到</li>
 *   <li>400 Bad Request: 请求参数错误</li>
 *   <li>500 Internal Server Error: 服务器内部错误</li>
 * </ul>
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理JWT认证异常
     *
     * <p>当JWT令牌验证失败时触发,包括:</p>
     * <ul>
     *   <li>令牌格式错误</li>
     *   <li>令牌已过期</li>
     *   <li>签名验证失败</li>
     * </ul>
     *
     * @param e JWT认证异常
     * @return 包含错误详情的响应对象
     */
    @ExceptionHandler(JwtAuthenticationException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ApiResponse(
        responseCode = "401",
        description = "JWT认证失败",
        content = @Content(
            mediaType = MediaType.APPLICATION_JSON_VALUE,
            schema = @Schema(implementation = ErrorResponse.class)
        )
    )
    public ResponseEntity<ErrorResponse> handleJwtAuthenticationException(JwtAuthenticationException e) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.UNAUTHORIZED.value(),
            "认证失败",
            e.getMessage()
        );
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
    }

    /**
     * 处理用户名密码认证失败异常
     *
     * <p>在以下情况下触发：</p>
     * <ul>
     *   <li>用户输入的密码错误</li>
     *   <li>用户账号被禁用或锁定</li>
     *   <li>认证信息不完整</li>
     * </ul>
     *
     * <p>错误响应：</p>
     * <ul>
     *   <li>状态码: 401 Unauthorized</li>
     *   <li>错误信息: "认证失败"</li>
     *   <li>详细信息: "用户名或密码错误"</li>
     * </ul>
     *
     * @param e 认证失败异常
     * @return 包含认证失败信息的统一错误响应
     */
    @ExceptionHandler(BadCredentialsException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ApiResponse(
        responseCode = "401",
        description = "用户名或密码错误",
        content = @Content(
            mediaType = MediaType.APPLICATION_JSON_VALUE,
            schema = @Schema(implementation = ErrorResponse.class)
        )
    )
    public ResponseEntity<ErrorResponse> handleBadCredentialsException(BadCredentialsException e) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.UNAUTHORIZED.value(),
            "认证失败",
            "用户名或密码错误"
        );
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
    }

    /**
     * 处理用户不存在异常
     *
     * <p>在以下场景下触发：</p>
     * <ul>
     *   <li>用户登录时使用了不存在的用户名</li>
     *   <li>访问或操作不存在的用户账号</li>
     *   <li>验证JWT令牌中的用户信息时用户不存在</li>
     * </ul>
     *
     * <p>错误响应：</p>
     * <ul>
     *   <li>状态码: 404 Not Found</li>
     *   <li>错误信息: "用户未找到"</li>
     *   <li>详细信息: 包含具体的用户名信息</li>
     * </ul>
     *
     * <p>安全建议：不要在生产环境暴露具体的用户名信息</p>
     *
     * @param e 用户不存在异常
     * @return 包含用户不存在信息的统一错误响应
     */
    @ExceptionHandler(UsernameNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ApiResponse(
        responseCode = "404",
        description = "用户未找到",
        content = @Content(
            mediaType = MediaType.APPLICATION_JSON_VALUE,
            schema = @Schema(implementation = ErrorResponse.class)
        )
    )
    public ResponseEntity<ErrorResponse> handleUsernameNotFoundException(UsernameNotFoundException e) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.NOT_FOUND.value(),
            "用户未找到",
            e.getMessage()
        );
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }

    /**
     * 处理请求参数校验失败异常
     *
     * <p>当请求对象的字段验证失败时触发,处理如下验证注解的失败情况：</p>
     * <ul>
     *   <li>@NotNull: 字段不能为null</li>
     *   <li>@NotEmpty: 集合/数组/字符串不能为空</li>
     *   <li>@NotBlank: 字符串不能为空白</li>
     *   <li>@Size: 长度/大小必须在指定范围</li>
     *   <li>@Min/@Max: 数值必须在指定范围</li>
     *   <li>@Email: 必须是有效的邮箱格式</li>
     *   <li>@Pattern: 必须匹配指定的正则表达式</li>
     * </ul>
     *
     * <p>错误响应处理：</p>
     * <ul>
     *   <li>收集所有字段的验证错误</li>
     *   <li>格式化为"字段名: 错误信息"的形式</li>
     *   <li>多个错误用分号分隔</li>
     * </ul>
     *
     * <p>响应示例：</p>
     * <pre>
     * {
     *   "status": 400,
     *   "message": "请求参数验证失败",
     *   "detail": "username: 用户名不能为空; password: 密码长度必须在6-20之间"
     * }
     * </pre>
     *
     * @param e 参数验证异常
     * @return 包含所有验证错误信息的响应对象
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ApiResponse(
        responseCode = "400",
        description = "请求参数验证失败",
        content = @Content(
            mediaType = MediaType.APPLICATION_JSON_VALUE,
            schema = @Schema(implementation = ErrorResponse.class)
        )
    )
    public ResponseEntity<ErrorResponse> handleValidationExceptions(MethodArgumentNotValidException e) {
        StringBuilder detail = new StringBuilder();
        e.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            detail.append(fieldName).append(": ").append(errorMessage).append("; ");
        });
        
        ErrorResponse error = new ErrorResponse(
            HttpStatus.BAD_REQUEST.value(),
            "请求参数验证失败",
            detail.toString().trim()
        );
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
    }

    /**
     * 处理所有未被其他异常处理器捕获的异常
     *
     * <p>作为系统最后的异常处理防线，捕获和处理所有未预期的异常，包括：</p>
     * <ul>
     *   <li>系统运行时异常(NullPointerException, IllegalArgumentException等)</li>
     *   <li>数据访问异常(DataAccessException及其子类)</li>
     *   <li>业务逻辑异常(自定义的业务异常)</li>
     *   <li>第三方组件异常</li>
     *   <li>其他未分类的异常</li>
     * </ul>
     *
     * <p>异常处理策略：</p>
     * <ul>
     *   <li>开发环境：返回详细的异常信息，便于调试</li>
     *   <li>生产环境：
     *     <ul>
     *       <li>记录完整的异常堆栈到日志系统</li>
     *       <li>返回统一的错误提示，避免暴露系统实现细节</li>
     *       <li>隐藏潜在的敏感信息</li>
     *       <li>保留异常追踪标识，便于问题定位</li>
     *     </ul>
     *   </li>
     * </ul>
     *
     * <p>安全建议：</p>
     * <ul>
     *   <li>生产环境中禁用异常堆栈信息的返回</li>
     *   <li>使用统一的错误消息模板</li>
     *   <li>实现异常监控和告警机制</li>
     *   <li>定期分析异常日志，优化系统稳定性</li>
     * </ul>
     *
     * @param e 捕获到的异常对象
     * @return 统一格式的错误响应
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ApiResponse(
        responseCode = "500",
        description = "服务器内部错误",
        content = @Content(
            mediaType = MediaType.APPLICATION_JSON_VALUE,
            schema = @Schema(implementation = ErrorResponse.class)
        )
    )
    public ResponseEntity<ErrorResponse> handleGenericException(Exception e) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            "内部服务器错误",
            e.getMessage()
        );
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}